package endcourseproject;
import lejos.nxt.LCD;
import lejos.nxt.SensorPort;
import lejos.nxt.Sound;




public class ZigBeeDevice extends Thread {

	// Constants
	public static final int EUI64SIZE = 8;              
    // OpcodeGroup ID's
	public static final byte MlmeNwkOpcodeGroup = (byte)0x84;    // MLME_NWK_SapHandler          
	public static final byte NwkMlmeOpcodeGroup = (byte)0x85;    // NWK_MLME_SapHandler          
	public static final byte McpsNwkOpcodeGroup = (byte)0x86;    // MCPS_NWK_SapHandler          
	public static final byte NwkMcpsOpcodeGroup = (byte)0x87;    // NWK_MCPS_SapHandler          
	public static final byte ZtcReqOpcodeGroup  = (byte)0xA3;    // Ztc utility requests   
	public static final byte ZtcCnfOpcodeGroup = (byte)0xA4;    // Ztc utility confirmations

    // ZTC Opcode ID's
	public static final byte ZtcMsgReadExtendedAdrReq = (byte)0xD2; // ZTC-ReadExtAddr.Request

	// MLME Opcode ID's
	public static final byte NwkStartCnf = (byte)0x0E;  // mac nwk start confirm

	// MCPS Opcode ID's
	public static final byte McpsDataReq = (byte)0x00;  // data request 
	public static final byte McpsDataCnf = (byte)0x00;  // data request confirm
	public static final byte McpsDataInd = (byte)0x01;  // data indication

	public static final byte STX = 2;
	
	public static final int txCountDownLoadValue = 100;  
	private int txCountDown = txCountDownLoadValue;  

	private ZigBeeZTCInterface ZigBeeModuleIf;
	
	private ZTCMsg rxMsg = new ZTCMsg();
    private byte[] EUI64 = null;
	
	public ZigBeeDevice(ZigBeeZTCInterface module)
	{
		ZigBeeModuleIf = module;
		
		this.setDaemon(true);
		this.start();
	}

	public ZigBeeDevice(SensorPort port)
	{
		this(new ZigBeeZTCInterface(port));
 	}

	
    private void ServiceAndMonitor()
    {

		if (EUI64 == null)
    	{
    		ZTC_RequestEUI64();
    	}
    }

	private void ZTC_RequestEUI64()
	{
		//LCD.drawString("lock 3 start", 0, 4);
		ZTCMsg msg = new ZTCMsg(ZtcReqOpcodeGroup, ZtcMsgReadExtendedAdrReq, 0, null );
		ZTC_Handler(msg);
		//LCD.drawString("lock 3 end  ", 0, 4);
	}
	
	public synchronized void BroadcastDataRequest(byte[] msdu)
	{
 		//LCD.drawString("lock 2 start", 0, 4);
		if (EUI64 != null && msdu != null )
		{
			ZTCMsg msg = new MCPS_dataRequest(MCPS_dataRequest.BroadcastAddr,EUI64,(byte) msdu.length,msdu);
			ZTC_Handler(msg);
		}
 		//LCD.drawString("lock 2 end  ", 0, 4);
	}
	
	private void MLME_NWK_SapHandler(ZTCMsg msg)
	{
		if (msg.getOpcode() == NwkStartCnf)
		{
			// confirm on nwk start - expect only one byte the status code - 0 == success
			if (msg.getLen() == 1 && msg.getData()[0] == 0) 
			{
	      		LCD.drawString("NWK start success", 0, 0);
			}
		}
		
	}
	private void MCPS_NWK_SapHandler(ZTCMsg msg)
	{
		if (msg.getOpcode() == McpsDataCnf)
		{
			// confirm on request to send data 
			// expect 1 bytes for the msduHandle and 1 for the status code (success = 0)
			if (msg.getLen() == 2 && msg.getData()[1] == 0) 
			{
				
			}
		}

		if (msg.getOpcode() == McpsDataInd)
		{
			if(msg.getLen()>= MCPS_dataIndication.MIN_SIZE ) 
			{
				/* filter out faulty packages - it seems the ZigBee I2C some times gives too short packages */
				MCPS_dataIndication dataInd = new MCPS_dataIndication(msg);
				if (dataInd.getData().length >= MCPS_dataIndication.MIN_SIZE + dataInd.getMsduLength())
				{	
				// data indication
		/*		
		      		LCD.drawString(Utils.getHex(dataInd.getSrcAddr(),2)+":", 0, 6);
		      		LCD.drawString(Utils.getHex(dataInd.getMpduLinkQuality())+":", 6, 6);
		      		LCD.drawString(Utils.getHex(dataInd.getMsdu(),4), 9, 6);
	    */  		
		      		if( _listener != null )
		      		{
		      			_listener.message(dataInd);
		         		//LCD.drawString("listener called", 0, 5);
		     		}
		      		else
		      		{
		          		//LCD.drawString("listener == null", 0, 5);
		      		}
				}
			}
      		
		}

	}
	
	private ZigBeeListener _listener = null;
	public void setListener(ZigBeeListener listener)
	{
		_listener  = listener;
	}
	
	private void ZTC_ResponseHandler(ZTCMsg msg)
	{
		if (msg.getOpcode() == ZtcMsgReadExtendedAdrReq)
		{
			// this is a response to read the IEEE Unique identifier from the ZigBee module
			// expect 8 bytes for the EUI and 1 for the status code (success = 0)
			if (msg.getLen() == EUI64SIZE+1 && msg.getData()[0] == 0) 
			{
				EUI64 = new byte[EUI64SIZE];
				for(int i = 0 ; i <EUI64SIZE ; i++ )
				{	
					try {EUI64[i] = msg.getData()[i+1];}
					catch (ArrayIndexOutOfBoundsException e) {
						LCD.drawString("exc in ZBDevice", 0, 4);
					}
				}
				
				LCD.drawString(Utils.getHex(Utils.reverseBytes(EUI64)), 0, 0);
			}
		}
	}
	
	private void ZTC_Handler(ZTCMsg msg)
	{
		LCD.drawString("ZTC_Handler UP  ", 0, 1);

/*
		LCD.drawString("ZTC_Handler UP  ", 0, 1);
		LCD.drawString("oG :     ", 0, 2);
		LCD.drawString("o  :     ", 0, 3);
		LCD.drawString("len:     ", 0, 4);
		LCD.drawString("CS :     ", 0, 5);
		LCD.drawInt(msg.getOpcodeGroup(), 	5, 2);
		LCD.drawInt(msg.getOpcode(), 		5, 3);
		LCD.drawInt(msg.getLen(), 			5, 4);
		LCD.drawInt(msg.CalcCS(), 		5, 5);
*/
		if(	msg.getOpcodeGroup() == MlmeNwkOpcodeGroup) 
        {
        	/* this is a response to a request of the MLME SAP
        	 * (MAC management service access point)
        	 */
        	MLME_NWK_SapHandler(msg);
        }

        if(	msg.getOpcodeGroup() == McpsNwkOpcodeGroup) 
        {
        	/* this is either a confirm or indication primitive from the MCPS SAP
        	 * (MAC data service access point)
        	 */
        	MCPS_NWK_SapHandler(msg);
        }
        if ( msg.getOpcodeGroup() == ZtcCnfOpcodeGroup)
        {
        	/* this is Response to request to the ZTC management opcodeGroup
        	 * (configuration not related to other SAP's)
        	 */
        	ZTC_ResponseHandler(msg);
        }

        if ( msg.getOpcodeGroup() == ZtcReqOpcodeGroup || 
        	 msg.getOpcodeGroup() == NwkMlmeOpcodeGroup ||
        	 msg.getOpcodeGroup() == NwkMcpsOpcodeGroup  )
        {   
        	/* these are all downward directed msg's 
        	 * the ZTC handler in the ZigBee module will distribute the msg's
        	 * so simply direct all towards the module
        	 */
        	boolean success = ZigBeeModuleIf.transmitMsg(msg);
    	  	LCD.drawString("ZTC_Handler DOWN", 0, 1);
			if(!success)
			{
				// TODO: What should we do if we fail ?
			}
		}
        
		LCD.drawString("ZTC_Handler out  ", 0, 1);

        
	}
	int cnt = 0;
	int cnt2 = 0;

	public void run()
	{	
   		//LCD.drawString("ZD Started", 0, 2);
 
		while(true)
		{
			LCD.drawInt(++cnt,12,6);
//			LCD.drawString(1+"  ",0,5);
			try {
				Thread.sleep(1);

				boolean success = false;
				// Check pending message
//				LCD.drawString(2+"  ",0,5);
				synchronized(this)
				{
//					LCD.drawString(3+"  ",0,5);
					//LCD.drawString("lock 1 start", 0, 4);

					boolean msg_ready = ZigBeeModuleIf.messageReady(); 
//					LCD.drawString(4+"  ",0,5);
					if(msg_ready)
					{
//						LCD.drawInt(++cnt2,12,5);
				 		 
						// Receive message
//						LCD.drawString(5+"  ",0,5);
						success = ZigBeeModuleIf.receiveMsg(rxMsg);
//						LCD.drawString(6+"  ",0,5);
					}
					else
					{
//						LCD.drawString(7+"  ",0,5);
						ServiceAndMonitor();
//						LCD.drawString(8+"  ",0,5);
					}

					if(success)
					{
						// Call handler
//						LCD.drawString(9+"  ",0,5);
						ZTC_Handler(rxMsg);
//						LCD.drawString(10+"  ",0,5);
					}

					//LCD.drawString("lock 1 end  ", 0, 4);
				}
				
				
			} catch (Exception e) {
				// TODO Auto-generated catch block
//				LCD.drawString(11+"  ",0,5);
			}
		}
	}	
}
